package com.heima.article.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.heima.apis.wemedia.IWmChannelClient;
import com.heima.article.mapper.ApArticleConfigMapper;
import com.heima.article.mapper.ApArticleContentMapper;
import com.heima.article.mapper.ApArticleMapper;
import com.heima.article.service.ApArticleService;
import com.heima.article.service.BuildArticleFreemakerService;
import com.heima.common.cache.CacheService;
import com.heima.common.constants.ArticleConstants;
import com.heima.common.constants.BehaviorConstants;
import com.heima.model.article.dtos.ArticleDto;
import com.heima.model.article.dtos.ArticleHomeDto;
import com.heima.model.article.pojos.ApArticle;
import com.heima.model.article.pojos.ApArticleConfig;
import com.heima.model.article.pojos.ApArticleContent;
import com.heima.model.article.vos.HotArticleVo;
import com.heima.model.behavior.dtos.ArticleInfoDto;
import com.heima.model.behavior.vos.ArticleBehaviorVo;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.user.pojos.ApUser;
import com.heima.model.wemedia.pojos.WmChannel;
import com.heima.utils.thread.ApUserThreadLocalUtil;
import freemarker.template.TemplateException;
import org.apache.commons.lang3.StringUtils;
import org.checkerframework.checker.units.qual.A;
import org.joda.time.DateTime;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class ApArticleServiceImpl implements ApArticleService {

    @Autowired
    private ApArticleMapper apArticleMapper;


    /**
     * 新的加载文章方法
     * @param type 1代表查询更多， 2代表查询最新
     * @param dto  查询条件
     * @param flag 代表是否是首页
     * @return
     */
    @Override
    public ResponseResult load2(Integer type, ArticleHomeDto dto, boolean flag) {
        //todo 1.代表文章是首页 ，查询redis
        String jsonStr = null;
        if(flag){
            if(dto.getTag().equals("__all__")){ //代表是推荐页面
                jsonStr = cacheService.get(ArticleConstants.HOT_ARTICLE_PAGE);
            }else{
                jsonStr = cacheService.get(ArticleConstants.HOT_ARTICLE_PAGE + dto.getTag());
            }
            if(StringUtils.isNotEmpty(jsonStr)){
                List<HotArticleVo> hotArticleVos = JSON.parseArray(jsonStr, HotArticleVo.class);
                return ResponseResult.okResult(hotArticleVos);
            }
        }
        //todo 2.不是首页，原来怎呢查询，还是怎么查询（查数据库）

        return load(type, dto);
    }

    @Override
    public ResponseResult load(Integer type, ArticleHomeDto dto) {
        //todo 校验参数
        if(dto == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        //todo 如果参数中tag 数据没有传输任何参数，那么就查询默认的所有分类
        if(StringUtils.isBlank(dto.getTag())){
            dto.setTag(ArticleConstants.DEFAULT_TAG);
        }
        //todo 如果时间没有传，也可以给默认值，以防止出现问题
        if(dto.getMinBehotTime() == null){
            dto.setMinBehotTime(new Date());
        }
        if(dto.getMaxBehotTime() == null) dto.setMaxBehotTime(new Date());


        List<ApArticle> apArticleList = apArticleMapper.loadArticleList(dto, type);
        return ResponseResult.okResult(apArticleList);
   }



    @Autowired
    private ApArticleConfigMapper apArticleConfigMapper;

    @Autowired
    private ApArticleContentMapper apArticleContentMapper;

    /**
     * 保存自媒体文章到文章库
     * @param dto
     * @return
     */
    @Override
    public ResponseResult saveArticle(ArticleDto dto) {
        //todo 1.校验参数
        if(dto == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        //todo 2.判断是否存在文章ID
        if(dto.getId() == null){
            //todo 2.1 如果不存在，代表新增
            //todo 2.1.1 保存数据到文章表
            apArticleMapper.insert(dto);
            //todo 2.1.2 保存数据到文章配置表
            ApArticleConfig apArticleConfig = new ApArticleConfig(dto.getId());
            apArticleConfigMapper.insert(apArticleConfig);
            //todo 2.1.3 保存数据到文章内容表
            ApArticleContent apArticleContent = new ApArticleContent();
            apArticleContent.setContent(dto.getContent());
            apArticleContent.setArticleId(dto.getId());
            apArticleContentMapper.insert(apArticleContent);
        }else{
            //todo 2.2 如果存在，代表修改
            //todo 2.2.1 修改文章表数据
            apArticleMapper.updateById(dto);
            //todo 2.2.2 修改文章内容表数据
            LambdaQueryWrapper<ApArticleContent> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(ApArticleContent::getArticleId,dto.getId());
            ApArticleContent apArticleContent = apArticleContentMapper.selectOne(wrapper);
            apArticleContent.setContent(dto.getContent());
            apArticleContentMapper.updateById(apArticleContent);
        }

        //todo 3.根据文章内容，来生成静态页面并且存储到minio中
        try {
            buildArticleFreemakerService.buildArticleToMinio(dto,dto.getContent());
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TemplateException e) {
            e.printStackTrace();
        }


        return ResponseResult.okResult(dto.getId());
    }

    /**
     * 加载行为记录
     * @param dto
     * @return
     */
    @Override
    public ResponseResult loadBehavior(ArticleInfoDto dto) {
        //1.todo 校验参数
        if(dto == null || dto.getArticleId() == null || dto.getAuthorId()==0){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        //2.todo 获得登录信息
        ApUser user = ApUserThreadLocalUtil.getUser();
        //2.1 todo 定义接收的数据
        boolean isLike = false , isunLike = false, isCollection = false , isFollow = false;
        if(user != null && user.getId() != 0){
            //3.todo 如果用户登录，查询行为
            //3.1 todo 查询用户喜欢行为
            Object oLike = cacheService.hGet(BehaviorConstants.LIKE_BEHAVIOR+dto.getArticleId().toString(), user.getId().toString());
            isLike = oLike != null ? true : false ;

            //3.2 todo 查询不喜欢行为
            Object ounLike = cacheService.hGet(BehaviorConstants.UN_LIKE_BEHAVIOR + dto.getArticleId().toString(), user.getId().toString());
            isunLike = ounLike !=null ? true : false;

            //3.3 todo 查询收藏行为
            Object oCollection = cacheService.hGet(BehaviorConstants.COLLECTION_BEHAVIOR + user.getId(), dto.getArticleId().toString());
            isCollection = oCollection != null ? true : false;

            //3.4 todo 查询关注行为  大key  Set集合 (value)
            Set<String> strings = cacheService.zRangeByScore(BehaviorConstants.APUSER_FOLLOW_RELATION + user.getId(), 0, new Date().getTime());
            //todo 1，根据用户ID查询获得此用户关注的所有作者 2.判断作者集合中是否存在该作者，如果存在，代表关注过了
            isFollow = strings.contains(dto.getAuthorId() + "");
        }
        //4.todo 如果没有登录，返回所有行为false
        //5.todo 封装返回结果
        ArticleBehaviorVo articleBehaviorVo = new ArticleBehaviorVo(isLike,isunLike,isCollection,isFollow);
        return ResponseResult.okResult(articleBehaviorVo);
    }

    /**
     * 热点文章计算接口
     *
     * 分值计算权重：
     *  阅读：1
     *  点赞：3
     *  评论：5
     *  收藏：8
     *  当日文章评分权重 * 3
     * @return
     *///真实执行时间未每天的0成0点
    @Override
    public ResponseResult findArticleListBy5day() {
        //1.todo 获得5天前的时间  (必须时分秒 ， 年月日即可)
        Date date5Day = new Date(new Date().getTime() - (60 * 1000 * 60 * 24 * 5));
        //Date date = DateTime.now().minusDays(5).toDate();  获得5天前的时间
        //Calendar 1.8 时间计算类 （建议使用方式）
        //2.todo 根据查询到的文章，进行每篇文章的分值计算
        List<ApArticle> articleList = apArticleMapper.findArticleBy5day(date5Day);
        List<HotArticleVo> hotArticleVos = new ArrayList<>();
        for (ApArticle article : articleList) {
            Integer score = 0;
            //2.1 todo 计算阅读的分值
            score += article.getViews() == null ? 0 : article.getViews() * ArticleConstants.HOT_ARTICLE_READ_WEIGHT;
            //2.2 todo 计算点赞的分值
            score += article.getLikes() == null ? 0 : article.getLikes() * ArticleConstants.HOT_ARTICLE_LIKES_WEIGHT;
            //2.3 todo 计算评论的分值
            score += article.getComment() == null ? 0 : article.getComment() * ArticleConstants.HOT_ARTICLE_COMMENT_WEIGHT;
            //2.4 todo 计算收藏的分值
            score += article.getCollection() == null ? 0 : article.getCollection() * ArticleConstants.HOT_ARTICLE_COLLECTION_WEIGHT;
            HotArticleVo hotArticleVo = new HotArticleVo();
            //2.5 todo 封装返回值VO对象
            BeanUtils.copyProperties(article,hotArticleVo);
            hotArticleVo.setScore(score);
            hotArticleVos.add(hotArticleVo);
        }
        //3.todo 存储到redis 1.推荐分类，单独分类
        //3.1 todo 存放每个频道的文章的前10名，查询所有频道
        ResponseResult responseResult = iWmChannelClient.findAll();
        if(responseResult.getCode() == 200 && responseResult.getData() != null){
            System.out.println(responseResult.getData() + "-----------------");
            String s = JSON.toJSONString(responseResult.getData());
            List<WmChannel> channelList = JSON.parseArray(s, WmChannel.class);
            //3.2 todo 遍历所有频道
            for (WmChannel wmChannel : channelList) {
                //3.3 todo 获得文章中所有满足频道ID的文章数据
                //3.4 todo 排序并且存储到redis缓存中 (zset)
                //3.5 todo 截取前10条即可
                List<HotArticleVo> channelArticles = hotArticleVos.stream()
                        .filter(c -> c.getChannelId() == wmChannel.getId()) //根据ID匹配过滤
                        .sorted(Comparator.comparing(HotArticleVo::getScore).reversed()) //排序
                        .limit(10)
                        .collect(Collectors.toList()); //收集过滤后的数据
                //3.6 todo 存储到redis
                cacheService.set(ArticleConstants.HOT_ARTICLE_PAGE + wmChannel.getId(),JSON.toJSONString(channelArticles));
            }
            //3.2 todo 推荐频道，所有文章的前10存储到redis 即可
            List<HotArticleVo> channelArticles = hotArticleVos.stream().sorted(Comparator.comparing(HotArticleVo::getScore).reversed())
                    .limit(10).collect(Collectors.toList());
            //3.3 todo 存储到redis 即可
            cacheService.set(ArticleConstants.HOT_ARTICLE_PAGE,JSON.toJSONString(channelArticles));
        }
        System.out.println("我执行完了.....");
        return ResponseResult.okResult("执行成功");
    }


    @Autowired
    private IWmChannelClient iWmChannelClient;


    @Autowired
    private CacheService cacheService;

    @Autowired
    private BuildArticleFreemakerService buildArticleFreemakerService;
}
